use crate::{Worker, Message, Handler};
use crate::worker::context::WorkerContextImpl;
use std::marker::PhantomData;
use crate::log_core::error;

pub trait ToEnvelop<W, M: Message>
    where W: Worker + Handler<M>,
          W::Context: ToEnvelop<W, M>
{
    fn pack(msg: M) -> Envelop<W>;
}

impl<W, M> ToEnvelop<W, M> for WorkerContextImpl<W>
    where W: Worker<Context=WorkerContextImpl<W>> + Handler<M>,
          M: Message + Send + 'static
{
    fn pack(msg: M) -> Envelop<W> {
        Envelop::new(msg)
    }
}

/// 结合tokio基本调度器能使用的CSP模型编程
pub struct Envelop<W>(Box<dyn EnvelopProxy<W, Worker=W> + Send>);

pub trait EnvelopProxy<W>
    where W: Worker {
    type Worker: Worker;

    fn handle(
        &mut self,
        act: &mut W,
    );
}

impl<W> Envelop<W> {
    pub(crate) fn new<M>(msg: M) -> Self
        where
            M: Message + Send + 'static,
            W: Worker + Handler<M>,
    {
        Envelop(Box::new(SyncEnvelopProxy {
            worker: PhantomData,
            msg: Some(msg),
        }))
    }
}

impl<W: Worker> EnvelopProxy<W> for Envelop<W> {
    type Worker = W;

    fn handle(
        &mut self,
        act: &mut W,
    ) {
        self.0.handle(act)
    }
}

unsafe impl<W, M> Send for SyncEnvelopProxy<W, M>
    where M: Message + Send
{}

struct SyncEnvelopProxy<W, M>
    where
        M: Message + Send + 'static,
{
    worker: PhantomData<W>,
    msg: Option<M>,
}

impl<W, M> EnvelopProxy<W> for SyncEnvelopProxy<W, M>
    where
        M: Message + Send + 'static,
        W: Worker + Handler<M>,
{
    type Worker = W;

    fn handle(
        &mut self,
        act: &mut W,
    ) {
        if let Some(msg) = self.msg.take() {
            if let Err(err)=<Self::Worker as Handler<M>>::handle(act, msg){
                error(err.to_string().as_str());
            };
        }
    }
}
